package org.jeecg.modules.filemanage.base.service.impl;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.jeecg.common.util.DateUtils;
import org.jeecg.common.util.UUIDGenerator;
import org.jeecg.modules.filemanage.base.entity.FmFileDetailsInfo;
import org.jeecg.modules.filemanage.base.entity.FmFileDirInfo;
import org.jeecg.modules.filemanage.base.service.IFmFileDetailsInfoService;
import org.jeecg.modules.filemanage.base.entity.FmFileExportRec;
import org.jeecg.modules.filemanage.base.service.*;
import org.jeecg.modules.filemanage.base.service.IFmFileExportRecService;
import org.jeecg.modules.filemanage.util.CodeUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.*;
import java.nio.charset.Charset;
import java.util.*;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

@Slf4j
@Service
public class UnzipFileServiceImpl implements IUnzipFileService {
    @Autowired
    private FileTransManageServiceImpl fileTransManageService;
    @Autowired
    private IFmFileDetailsInfoService fileDetailsInfoService;
    @Autowired
    private IFmFileExportRecService dtgFileExportRecService;

    @Value(value = "${fileInOut.path}")
    private String path;
    @Value(value = "${fileInOut.rootpan}")
    private String rootpan;

    @Autowired
    @Lazy
    private CodeUtils codeUtils;


    private static final int  BUFFER_SIZE = 1 * 1024;

   public boolean unZip(MultipartFile srcFile) {
       boolean res = true;
       try {
           ZipFile zipFile = null;
           ZipInputStream zin = null;
           FileInputStream fis = null;
           File file = multipartFileToFile(srcFile);
           long start = System.currentTimeMillis();
           //唯一时间戳标识
           Map<String,String> fileList = new HashMap<>();
           // 开始解压
           unzip(file, rootpan + path,fileList);
           long end = System.currentTimeMillis();
           System.out.println("解压完成，耗时：" + (end - start) + " ms");
           zipFile = new ZipFile(file, Charset.forName("GBK"));
           //读xml信息
         //  AnalysisXml(rootpan,path,fileList);
           //写入文件数据
           WriteFileInfo(zipFile,fileList);
       } catch (Exception e) {
           res = false;
           throw new RuntimeException("解压失败，原因："+e.getMessage(), e);
       }
       return res;
   }

    /**
     * MultipartFile 转 File
     *
     * @param file
     * @throws Exception
     */
    public static File multipartFileToFile(MultipartFile file) throws Exception {

        File toFile = null;
        if (file.equals("") || file.getSize() <= 0) {
            file = null;
        } else {
            InputStream ins = null;
            ins = file.getInputStream();
            toFile = new File(file.getOriginalFilename());
            inputStreamToFile(ins, toFile);
            ins.close();
        }
        return toFile;
    }

    //获取流文件
    private static void inputStreamToFile(InputStream ins, File file) {
        try {
            OutputStream os = new FileOutputStream(file);
            int bytesRead = 0;
            byte[] buffer = new byte[8192];
            while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
            os.close();
            ins.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //将文件信息写入文件记录表
    private   void  WriteFileInfo(ZipFile zipFile,Map<String,String> fileList) {
        Enumeration<?> entries = zipFile.entries();
        //CodeUtils codeUtils = new CodeUtils();
        List<FmFileDirInfo> Listfd = new ArrayList<>();
        List<FmFileDetailsInfo> Listfdi = new ArrayList<>();
        Map<String, Object> map = new HashMap<>();
        String temppath = path.substring(path.lastIndexOf("/")+1) +"/";
        while (entries.hasMoreElements()) {
            ZipEntry entry = (ZipEntry) entries.nextElement();

            System.out.println("解压" + entry.getName());
            FmFileDirInfo fd = null;
            FmFileDetailsInfo fdi = null;
            // 如果是文件夹，就略过
            if (entry.isDirectory()) {
                continue;
            } else {
                //如果是文件就记录下来
                String filename = entry.getName(),fileid = UUIDGenerator.generate();
                long filesize = entry.getSize();

                    fd = new FmFileDirInfo();
                    fdi = new FmFileDetailsInfo();

                    filename = fileList.get(entry.getName());
                    fd.setId(UUIDGenerator.generate());
                    fd.setVersionNo(1.0);
                    fd.setFileid(fileid);
                    fd.setVersionNo(1.0);
                    fdi.setFileCode(codeUtils.createCode("filemanage"));
                    fdi.setFilePath(temppath + filename);
                    fdi.setFileDescription("导入" + zipFile.getName());
                    fdi.setBoatBelong(zipFile.getName());
                    fdi.setFileTitle(filename.substring(0,filename.lastIndexOf("_")) +filename.substring(filename.lastIndexOf(".")));
                    fdi.setFileMixName(filename);
                    fdi.setFileSize((int) filesize);
                    fdi.setModelName("filemanage");
                    fdi.setFileid(fileid);
                    if (filename.contains("."))
                        fdi.setFileTypes(filename.substring(filename.lastIndexOf(".") + 1));
                   Listfd.add(fd);
                   Listfdi.add(fdi);

            }
        }
        map.put("obj", Listfd);
        map.put("obb", Listfdi);
        boolean res = this.fileTransManageService.batchadd(map);
    }

    public static void unzip(File zipFile, String descDir , Map<String,String> fileList){
        try (ZipArchiveInputStream inputStream = getZipFile(zipFile)) {
            File pathFile = new File(descDir);
            if (!pathFile.exists()) {
                pathFile.mkdirs();
            }
            ZipArchiveEntry entry = null;
            while ((entry = inputStream.getNextZipEntry()) != null) {
                if (entry.isDirectory()) {
//                    File directory = new File(descDir, entry.getName().substring(0,entry.getName().indexOf("/"))+"_"+timesteap +entry.getName().substring(entry.getName().indexOf("/")));
//                    directory.mkdirs();
                    continue;
                } else {
                    OutputStream os = null;
                    try {
                        String fileName =null,tempName = null;
                        tempName =  new String(entry.getRawName(),Charset.forName("GBK")); //此变量解决打包后乱码问题
                        if(tempName.contains("file_")&&tempName.contains(".")) {
                             fileName = tempName.substring(tempName.lastIndexOf("/")+1, tempName.lastIndexOf(".")) + "_" + System.currentTimeMillis() + tempName.substring(tempName.indexOf("."));
                        }else if(!tempName.contains("file_")&&tempName.contains(".")){
                            if(tempName.contains("/"))
                                fileName = tempName.substring(tempName.lastIndexOf("/")+1, tempName.lastIndexOf(".")) + "_" + System.currentTimeMillis() + tempName.substring(tempName.indexOf("."));
                            else
                                fileName = tempName.substring(0, tempName.lastIndexOf(".")) + "_" + System.currentTimeMillis() + tempName.substring(tempName.indexOf("."));
                        }else{
                             fileName = tempName+ "_" + System.currentTimeMillis();
                        }
                        os = new BufferedOutputStream(new FileOutputStream(new File(descDir, fileName)));
                        fileList.put(tempName,fileName);
                        //输出文件路径信息
                        log.info("解压文件的当前路径为:{}", descDir + fileName);
                        IOUtils.copy(inputStream, os);
                    }catch(Exception e){
                        e.printStackTrace();
                        log.error("解压文件失败路径为:{}", entry.getName());
                    }
                    finally {
                        IOUtils.closeQuietly(os);
                    }
                }
            }
            final File[] files = pathFile.listFiles();
            if (files != null && files.length == 1 && files[0].isDirectory()) {
                // 说明只有一个文件夹
                FileUtils.copyDirectory(files[0], pathFile);
                //免得删除错误， 删除的文件必须在/data/demand/目录下。
                boolean isValid = files[0].getPath().contains("/data/www/");
                if (isValid) {
                    FileUtils.forceDelete(files[0]);
                }
            }
            log.info("******************解压完毕********************");

        } catch (Exception e) {
            log.error("[unzip] 解压zip文件出错", e);
        }
    }

    private static ZipArchiveInputStream getZipFile(File zipFile) throws Exception {
        return new ZipArchiveInputStream(new BufferedInputStream(new FileInputStream(zipFile)));
    }

    public String exportzip(ArrayList<String> fileIds , String branchId){

        List<FmFileDetailsInfo> fileDetailsInfos = fileDetailsInfoService.listByIds(fileIds);
        List<File> srcFiles = null;
        String url = (rootpan+path).replace("files//","");
        if(fileDetailsInfos.size()>0)
            srcFiles = fileDetailsInfos.stream().map(o->new File(url+"/"+o.getFileMixName())).collect(Collectors.toList());
        else
            return "导出数据包失败，所选文件信息不存在";
        toZip(srcFiles,new File(rootpan+ DateUtils.getDate("yyyyMMdd-HHmmss") +".zip"));
        //写导出记录
        boolean res = exportRec(fileDetailsInfos,branchId);
        if(!res) return "写导出记录失败";

        return "导出数据包成功";
    }


    public static void toZip(List<File> srcFiles, File zipFile) throws RuntimeException {
        long start = System.currentTimeMillis();
        if(zipFile == null){
            log.error("压缩包文件名为空！");
            return;
        }
        if(!zipFile.getName().endsWith(".zip")){
            log.error("压缩包文件名异常，zipFile={}", zipFile.getPath());
            return;
        }
        ZipOutputStream zos = null;
        try {
            FileOutputStream out = new FileOutputStream(zipFile);
            zos = new ZipOutputStream(out,Charset.forName("GBK"));
            for (File srcFile : srcFiles) {
                byte[] buf = new byte[BUFFER_SIZE];
                zos.putNextEntry(new ZipEntry(srcFile.getName()));
                int len;
                FileInputStream in = new FileInputStream(srcFile);
                while ((len = in.read(buf)) != -1) {
                    zos.write(buf, 0, len);
                }
                zos.setComment("资料库系统打包");
                zos.closeEntry();
                in.close();
            }
            zos.close();
            out.close();//输出流一定要放在循环外关闭
            long end = System.currentTimeMillis();
            log.info("压缩完成，耗时：" + (end - start) + " ms");
        } catch (Exception e) {
            log.error("ZipUtil toZip exception, ", e);
            throw new RuntimeException("zipFile error from ZipUtils", e);
        } finally {
            if (zos != null) {
                try {
                    zos.close();
                } catch (IOException e) {
                    log.error("ZipUtil toZip close exception, ", e);
                }
            }
        }
    }

    private boolean exportRec(List<FmFileDetailsInfo> fileDetailsInfos,String branchId){
        boolean res = false;
        List<FmFileExportRec> fmFileExportRecList = new ArrayList<>();
        fmFileExportRecList = fileDetailsInfos.stream().map(o->{
            FmFileExportRec dfe = new FmFileExportRec();
            dfe.setFileid(o.getFileid());
            dfe.setBelong(o.getEqpBelong());
            dfe.setPid(o.getPid());
            dfe.setFileTitle(o.getFileTitle());
            dfe.setBranchId(branchId);
            if(o.getEqpUid().isEmpty())
                dfe.setTypename("目录");
            else
                dfe.setTypename("设备");
            return dfe;
        }).collect(Collectors.toList());
        res = dtgFileExportRecService.saveBatch(fmFileExportRecList);
        return res;
    }

//    String factoryid = "";
//    private  void AnalysisXml(String rootpan,String path,Map<String,String> fileList){
//        Map<String,String> res = new HashMap<>();
//        String flag = ""; factoryid = "";
//        for(Map.Entry<String, String> entry : fileList.entrySet()) {
//            String mapKey = entry.getKey(); //混淆前带路径
//            String mapValue = entry.getValue(); //混淆后不带路径的纯文件名
//            if (!mapKey.contains("file_") && mapKey.contains(".xml")) {
//                //非file下的关联文件 并且是xml 就认为是配置文件
//                //1.创建DocumentBuilderFactory对象
//                DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
//                //2.创建DocumentBuilder对象
//                try {
//                    DocumentBuilder builder = factory.newDocumentBuilder();
//                    Document d = builder.parse(rootpan + path + '/' + mapValue);
//                    //取工厂信息
//                    if(mapKey.contains("factoryInfo")) {
//                        NodeList sList = d.getElementsByTagName("data");
//                        res.clear();
//                        res = node(sList,"factoryInfo");
//
//                    }
//                    //取设备信息
//                    if(mapKey.contains("deviceInfo")){
//                        NodeList sList = d.getElementsByTagName("data");
//                        res.clear();
//                        res = node(sList,"deviceInfo");
//                    }
//
//                } catch (Exception e) {
//                    e.printStackTrace();
//                }
//
//            }
//        }
//
//
//    }
//
//    public  Map<String,String> node(NodeList list,String flag){
//        Map<String,String> res = new HashMap<>();
//        for (int i = 0; i <list.getLength() ; i++) {
//            Node node = list.item(i);
//            NodeList childNodes = node.getChildNodes();
//            if(node.getAttributes().getNamedItem("id")!=null) {
//                res.put("id",node.getAttributes().getNamedItem("id").getNodeValue());
//            }
//            for (int j = 0; j <childNodes.getLength() ; j++) {
//                if (childNodes.item(j).getNodeType()==Node.ELEMENT_NODE) {
//                    res.put(childNodes.item(j).getNodeName(),childNodes.item(j).getFirstChild().getNodeValue());
//                }
//            }
//            //组装
//            if(flag.equalsIgnoreCase("factoryInfo"))
//                assembleAtttribute(res,DtgSupplierInfo.class);
//            if(flag.equalsIgnoreCase("deviceInfo"))
//                assembleAtttribute(res,DtgEquipmentInfo.class);
//        }
//        return  res;
//    }
//
//    private  boolean assembleAtttribute(Map<String,String> source,Class<?> typeClass){
//        boolean res = false;
//        if(typeClass == DtgSupplierInfo.class){
//            //工厂信息
//            factoryid = source.getOrDefault("id","");
//            DtgSupplierInfo ds = null;
//            ds = dtgSupplierInfoService.getById(factoryid);
//            if(ds!=null) {
//                return true;
//            }
//            ds = new DtgSupplierInfo();
//            ds.setId(source.getOrDefault("id",""));
//            ds.setName(source.getOrDefault("factoryname",""));
//            ds.setCountryName(source.getOrDefault("country",""));
//            ds.setAddress(source.getOrDefault("address",""));
//            ds.setBusinessScope(source.getOrDefault("businescope",""));
//            ds.setTaxationNo(source.getOrDefault("taxid",""));
//            ds.setQualification(source.getOrDefault("factoryidentity",""));
//            ds.setPostCode(source.getOrDefault("mailcode",""));
//            ds.setTelphone(source.getOrDefault("phonenum",""));
//           res =  dtgSupplierInfoService.save(ds);
//        }else if(typeClass == DtgEquipmentInfo.class){
//            //设备信息
//            DtgEquipmentInfo de = null;
//            de = dtgEquipmentInfoService.getById(source.getOrDefault("id",""));
//            if(de!=null){
//                return true;
//            }
//            de = new DtgEquipmentInfo();
//            de.setId(source.getOrDefault("id",""));
//            de.setSupplier(factoryid);
//            de.setName(source.getOrDefault("devicename",""));
//            de.setEqpNo(source.getOrDefault("devicenum",""));
//            de.setHeight(Double.valueOf(source.getOrDefault("height","0")));
//            de.setLength(Double.valueOf(source.getOrDefault("length","0")));
//            de.setWeight(source.getOrDefault("weight",""));
//            de.setMtbf(source.getOrDefault("mtbf",""));
//            de.setMttr(source.getOrDefault("mttr",""));
//           res = dtgEquipmentInfoService.save(de);
//        }
//        return res;
//    }

}
